import { Request, Response } from 'express';
import mongoose from 'mongoose';
import { z } from 'zod';

import { ProjectModel } from '../../models/project';
import { sendErrorResponse, sendSuccessResponse } from '../../utils/response';

/**
 * 添加项目脚本组的请求体验证模式
 */
export const addProjectScriptSchema = z.object({
  // 项目ID
  id: z.string().regex(/^[0-9a-fA-F]{24}$/, '应用ID格式不正确'),
  // 脚本组
  scripts: z.object({
    desc: z.string().max(500, '脚本组描述不能超过500个字符'),
    scripts: z.array(z.string().regex(/^[0-9a-fA-F]{24}$/, '应用ID格式不正确')),
    title: z.string().min(1, '脚本组标题不能为空').max(100, '脚本组标题不能超过100个字符'),
  }),
});

export type AddProjectScriptBody = z.infer<typeof addProjectScriptSchema>;

/**
 * 验证项目是否存在
 * @param projectId 项目ID
 * @returns Promise<boolean> 项目是否存在
 */
async function validateProjectExists(projectId: string): Promise<boolean> {
  try {
    const project = await ProjectModel.findById(projectId).lean();
    return !!project;
  } catch (error) {
    console.error('验证项目存在性失败:', error);
    return false;
  }
}

/**
 * 检查脚本组标题在项目中是否唯一
 * @param projectId 项目ID
 * @param title 脚本组标题
 * @returns Promise<boolean> 标题是否唯一
 */
async function isScriptGroupTitleUnique(projectId: string, title: string): Promise<boolean> {
  try {
    const project = await ProjectModel.findById(projectId, 'scripts').lean();
    if (!project || !project.scripts) return true;

    // 检查当前脚本组标题是否已存在
    const scripts = project.scripts as any;
    return scripts.title !== title;
  } catch (error) {
    console.error('检查脚本组标题唯一性失败:', error);
    return false;
  }
}

/**
 * 添加项目脚本组
 * @description 为指定项目添加新的脚本组
 * @route POST /api/project/script
 * @param req Express请求对象
 * @param res Express响应对象
 * @returns Promise<Response> 添加结果响应
 *
 * @example
 * POST /api/project/script
 * {
 *   "id": "507f1f77bcf86cd799439011",
 *   "scripts": {
 *     "title": "构建脚本组",
 *     "desc": "包含所有构建相关的脚本",
 *     "scripts": ["507f1f77bcf86cd799439012"]
 *   }
 * }
 *
 * 成功响应:
 * {
 *   "success": true,
 *   "code": 200,
 *   "message": "脚本组添加成功",
 *   "data": {
 *     "project": {
 *       "_id": "507f1f77bcf86cd799439011",
 *       "name": "示例项目",
 *       "scripts": {
 *         "title": "构建脚本组",
 *         "desc": "包含所有构建相关的脚本",
 *         "scripts": ["507f1f77bcf86cd799439012"]
 *       },
 *       "updatedAt": "2024-01-01T00:00:00.000Z"
 *     }
 *   }
 * }
 */
export async function addProjectScript(req: Request, res: Response): Promise<Response> {
  const session = await mongoose.startSession();

  try {
    const { id, scripts } = req.body as AddProjectScriptBody;

    // 开始事务
    await session.startTransaction();

    // 并行执行验证操作以提高性能
    const [projectExists, isTitleUnique] = await Promise.all([
      validateProjectExists(id),
      isScriptGroupTitleUnique(id, scripts.title),
    ]);

    // 检查项目是否存在
    if (!projectExists) {
      await session.abortTransaction();
      return sendErrorResponse(res, 404, '项目不存在');
    }

    // 检查脚本组标题在项目中是否唯一
    if (!isTitleUnique) {
      await session.abortTransaction();
      return sendErrorResponse(res, 400, '该项目中已存在同名脚本组');
    }

    // 更新项目的脚本组信息
    const updatedProject = await ProjectModel.findByIdAndUpdate(
      id,
      {
        $set: {
          scripts: {
            desc: scripts.desc,
            scripts: scripts.scripts,
            title: scripts.title,
          },
        },
      },
      {
        new: true,
        select: 'name scripts updatedAt',
        session,
      },
    );

    if (!updatedProject) {
      await session.abortTransaction();
      return sendErrorResponse(res, 404, '项目不存在');
    }

    // 提交事务
    await session.commitTransaction();

    const responseData = {
      project: updatedProject,
    };

    console.log(`脚本组添加成功: ${scripts.title} -> 项目: ${updatedProject.name} (ID: ${id})`);
    return sendSuccessResponse(res, responseData, '脚本组添加成功');
  } catch (error) {
    // 回滚事务
    await session.abortTransaction();
    console.error('添加项目脚本组失败:', error);

    // 处理不同类型的错误
    if (error instanceof Error) {
      switch (error.name) {
        case 'ValidationError':
          return sendErrorResponse(res, 400, '数据验证失败');
        case 'CastError':
          return sendErrorResponse(res, 400, '数据格式错误');
        case 'MongoServerError':
          return sendErrorResponse(res, 500, '数据库操作失败');
        default:
          return sendErrorResponse(res, 500, '服务器内部错误');
      }
    }

    return sendErrorResponse(res, 500, '服务器内部错误');
  } finally {
    // 确保会话被正确关闭
    await session.endSession();
  }
}
